#include "communication.h"

void init_server(module* mdl, const int port, const int max_connexions) {
	server srv = *(mdl->srv);
	strcpy(srv.name,mdl->name);
	srv.isAlive = 1;
	if ((srv.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}
	srv.local_addr.sin_family = AF_INET; /* host byte order */
	srv.local_addr.sin_port = htons(port); /* short, network byte order */
	srv.local_addr.sin_addr.s_addr = INADDR_ANY; /* auto-remplissage avec mon IP */
	bzero(&(srv.local_addr.sin_zero), 8); /* zero pour le reste de struct */

	if (bind(srv.sockfd,( struct sockaddr *)&(srv.local_addr),sizeof(struct sockaddr))
			== -1) {
		perror("bind");
		exit(1);
	}

	if (listen(srv.sockfd, max_connexions) == -1) {
		perror("listen");
		exit(1);
	}
	log_srv(&srv,"créé.");
	if(!fork()) { /* fils première génération - permet de revenir de la méthode par contre créé des zombies */
		while(1) { // boucle d'accept() pour recevoir les connexions
			int pid; // pid du fil
			srv.sin_size = sizeof(struct sockaddr_in);
			int new_fd; // descripteur lié au socket de la connexion courante
			if ((new_fd = accept(srv.sockfd, (struct sockaddr *)&(srv.remote_addr),
					&(srv.sin_size))) == -1) {
				perror("accept");
				continue;
			}
			if(!srv.isAlive) {
				close(new_fd); //si une connexion arrive alors que le programme est terminé, on sort
				exit(0);
			}
			char tmp[128];
			sprintf(tmp, "connexion entrante depuis %s:%d",inet_ntoa(srv.remote_addr.sin_addr),srv.remote_addr.sin_port);
			log_srv(&srv,tmp);
			if (!(pid=fork())) { /* fils seconde génération - permet d'être multiclient */
				while(srv.isAlive) {
					char answer[MAXRECVDATA];
					sprintf(answer,"Serveur %s. En attente d'un msg (voir libcomm/message.h).\n", mdl->name);
					if (send(new_fd, answer, strlen(answer), 0) == -1)
						perror("send");

					srv.recvdata = (msg*) malloc (sizeof (msg));
					bzero(srv.recvdata,sizeof (msg));
					if (srv.isAlive && (srv.numbytes=recv(new_fd, srv.recvdata, MAXRECVDATA, 0)) == -1) {
						perror("recv");
						log_srv(&srv,"erreur lors du recv!");
						exit(1);
					}

					sprintf(tmp,"Reception par %s %s:%d",mdl->name, inet_ntoa(srv.remote_addr.sin_addr),srv.remote_addr.sin_port);
					log_msg(tmp,srv.recvdata);
					switch(mdl->type){
					case ack: printf("type ack"); break;
					case exec:printf("type exec");  break;
					case interb:printf("type interb");  break;
					default: log_smth("Tentative de démarrage d'un serveur non typé!");
					}
					//TODO ajouter l'exec() pour appeler le bon processus de gestion
				} // fin du while pour le recv()
				close(new_fd);
				//exit(0);
			}
			close(new_fd); // le parent n'a pas besoin de new_fd

			while(waitpid(-1,NULL,WNOHANG) > 0); // nettoyage des processus fils
			srv.isAlive=0;
			kill(pid,SIGKILL);
			exit(0);
		} // fin while du "accept"
	} // fin fils première génération
}

void init_client(module* mdl, module* acks) {
	server srv = *(mdl->srv);
	strcpy(srv.name,mdl->name);
	struct hostent *he;
	if ((he=gethostbyname("localhost")) == NULL) {
		herror("gethostbyname");
		log_srv(&srv, "erreur hôte introuvable");
	}

	if ((srv.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		log_srv(&srv, "erreur lors de l'init du socket");
	}

	srv.remote_addr.sin_family = AF_INET; /* host byte order */
	//srv.remote_addr.sin_port = htons(acks[mdl->bourse_id-1]->srv->my_port); /* short, network byte order */
	//srv.remote_addr.sin_port = htons(acks[0].srv->my_port); /* short, network byte order */
	srv.remote_addr.sin_port = htons(1201); /* short, network byte order */
	srv.remote_addr.sin_addr = *((struct in_addr *)(he)->h_addr);
	bzero(&(srv.remote_addr.sin_zero), 8); /* zero pour le reste de struct */

	log_srv(&srv,"créé.");
	if (connect(srv.sockfd,( struct sockaddr *)&(srv.remote_addr),
			sizeof(struct sockaddr)) == -1) {
		perror("connect");
		log_srv(&srv,"déjà connecté ou impossible de se connecter");
	}
	// tests
	msg * damsg = create_msg("1", "Achat", "0001000012", "12");
	if (sendto(srv.sockfd, damsg, sizeof(msg), 0, (struct sockaddr *)&(srv.remote_addr), (socklen_t)sizeof(struct sockaddr)) == -1) {
			perror("send");
			log_srv(&srv, "erreur lors de l'envoi du message");
	}
}

void send_msg(const module* mdl, const server* srv_dest, msg* damsg) {
	if (damsg == NULL) {
		log_srv(mdl->srv, "Tentative d'envoi d'un message NULL");
		return;
	}
	char tmp[128];
	sprintf(tmp, "Envoi vers %s", inet_ntoa(mdl->srv->remote_addr.sin_addr));
	log_msg(tmp, damsg);

	if (sendto((mdl->srv)->sockfd, damsg, sizeof(msg), 0, (struct sockaddr *)&(mdl->srv->remote_addr), (socklen_t)sizeof(struct sockaddr)) == -1) {
		perror("send");
		log_srv(mdl->srv, "erreur lors de l'envoi du message");
	}
	//close(clt.sockfd);
}

module* init_module(char *nom, int type, int bourse){
	module *rtn=(module*) malloc (sizeof (module));
	rtn->type=type;
	rtn->bourse_id=bourse;
	rtn->srv = malloc (sizeof (server));
	sprintf(rtn->name,"%s",nom);
	if(strlen(nom)>128) log_smth("ATTENTION: le nom du module \"%s\" est trop long",nom);
	return rtn;
}

void log_srv(const server* srv, char* un_msg) {
	char logged[1024];
	sprintf(logged, "Serveur %s %s", srv->name, un_msg);
	private_write_log(comm_type, logged);
}

